#ifndef CALIBRATOR_H
#define CALIBRATOR_H
#include <string>
#include <QObject>
#include "settings.h"

int calMainRun(int argc, char* argv[]);

void doCal(string path);

class Calibrator : public QObject {
    Q_OBJECT
private:
    const int m_argc;
    char** m_argv = nullptr;
    enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 };

public:
    explicit Calibrator(int argc, char** m_argv, QObject* parent = nullptr);
    ~Calibrator();
    // 标定主函数
    int calMainRun();
    // 校准函数
    void doCal(string path);

private:
    /**
     * @brief runCalibrationAndSave，标定并保存配置参数
     * @param s，配置参数
     * @param imageSize，图像尺寸
     * @param cameraMatrix，相机内参
     * @param distCoeffs，畸变系数
     * @param imagePoints，每幅标定图像中的特征点坐标
     * @param grid_width
     * @param release_object
     * @return
     */
    bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs,
        vector<vector<Point2f> > imagePoints, float grid_width, bool release_object);

    /**
     * @brief computeReprojectionErrors，计算映射误差
     * @param objectPoints，标定板上特征点的世界坐标
     * @param imagePoints，标定图像上检测出的特征点坐标
     * @param rvecs，外参：旋转矩阵
     * @param tvecs，外参：平移矩阵
     * @param cameraMatrix，相机内参
     * @param distCoeffs，畸变系数
     * @param perViewErrors
     * @param fisheye，是否鱼眼镜头
     * @return 映射误差
     */
    double computeReprojectionErrors(const vector<vector<Point3f> >& objectPoints,
        const vector<vector<Point2f> >& imagePoints,
        const vector<Mat>& rvecs, const vector<Mat>& tvecs,
        const Mat& cameraMatrix, const Mat& distCoeffs,
        vector<float>& perViewErrors, bool fisheye);

    /**
     * @brief calcBoardCornerPositions，根据标定板类型计算特征点的世界坐标
     * @param boardSize，标定板尺寸（宽高分别有几个特征点）
     * @param squareSize，标定板一个单元的尺寸
     * @param corners，输出的特征点世界坐标
     * @param patternType，标定板类型
     */
    void calcBoardCornerPositions(Size boardSize, float squareSize, vector<Point3f>& corners,
        Settings::Pattern patternType /*= Settings::CHESSBOARD*/);

    // 标定
    /**
     * @brief runCalibration，标定计算
     * @param s，配置参数
     * @param imageSize，图像尺寸
     * @param cameraMatrix，相机内参
     * @param distCoeffs，畸变系数
     * @param imagePoints，每幅标定图像的特征点坐标
     * @param rvecs，外参：旋转矩阵
     * @param tvecs，内参：平移矩阵
     * @param reprojErrs，每幅图的映射误差
     * @param totalAvgErr，平均映射误差
     * @param newObjPoints
     * @param grid_width
     * @param release_object
     * @return 标定是否成功
     */
    bool runCalibration(Settings& s, Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs,
        vector<vector<Point2f> > imagePoints, vector<Mat>& rvecs, vector<Mat>& tvecs,
        vector<float>& reprojErrs, double& totalAvgErr, vector<Point3f>& newObjPoints,
        float grid_width, bool release_object);

    /**
     * @brief saveCameraParams，保存相机标定参数
     * @param s，配置参数
     * @param imageSize，图像尺寸
     * @param cameraMatrix，相机内参
     * @param distCoeffs，畸变系数
     * @param rvecs，外参：旋转矩阵
     * @param tvecs，内参：平移矩阵
     * @param reprojErrs，每幅图的映射误差
     * @param imagePoints，每幅标定图像的特征点坐标
     * @param totalAvgErr，平均映射误差
     * @param newObjPoints
     */
    void saveCameraParams(Settings& s, Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs,
        const vector<Mat>& rvecs, const vector<Mat>& tvecs,
        const vector<float>& reprojErrs, const vector<vector<Point2f> >& imagePoints,
        double totalAvgErr, const vector<Point3f>& newObjPoints);
};

#endif // CALIBRATOR_H
